<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="keywords" content="好好学习">
    <meta name="description" content="这是一个学习的第一课">
    <title>JS学习</title>
    <style>
        body,p,h1,h2,h3,h4,h5,h6,ul{margin: 0;padding: 0;}
        body{font:12px"宋体","Arial Narrow",HELVETICA;background:#fff;-webkit-text-size-adjust:100%;}
        li{list-style: none;}
        a{text-decoration: none;}
        img{border: 0;vertical-align: middle;}
        #wrap{
            position: relative;
            margin: 0 auto;
            width: 512px;
            height: 768px;
            background: url("./images/bg_1.jpg");
            user-select: none;
            overflow: hidden;
        }
        #mainBox{
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
        }
        #mainBox h2{
            margin: 100px;
            font-family: "黑体";
            text-align: center;
            font-size: 40px;
            color: #fff;
        }
        #mainBox li{
            margin: 30px auto;
            width: 200px;
            height: 40px;
            font-size: 20px;
            font-family: "heiti";
            line-height: 40px;
            text-align: center;
            color: #fff;
            background: orangered;
            cursor: pointer;
        }
        #mainBox li:hover{
            background: #000;
            color: #f00;
        }
        #gameBox{
            display: none;
        }
        #gameBox .background{
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            height: 200%;
            background: url("./images/bg_2.jpg");
        }
        #gameBox .score{
            position: absolute;
            left: 10px;
            top: 10px;
            font-size: 30px;
            color: #fff;
        }
        #gameOver{
            display: none;
        }
        #gameOver p{
            margin-bottom: 50px;
            font-size: 50px;
            color: #fff;
            text-align: center;
        }
        #gameOver p:nth-child(1){
            margin-top: 150px;
        }
        #gameOver p:nth-child(3){
            color: #f00;
        }
        #gameOver span{
            display: block;
            margin: 0 auto;
            width: 150px;
            height: 60px;
            line-height: 60px;
            text-align: center;
            font-size: 30px;
            color: #f00;
            background: #ccc;
            cursor: pointer;
        }
        #gameOver span:hover{
            background: #000;
            color: #fff;
        }
    
    </style>
</head>
<body>
    <div id="wrap">
        <div id="mainBox">
            <h2>飞 机 大 战</h2>
            <ul>
                <li>简 单</li>
                <li>中 等</li>
                <li>困 难</li>
            </ul>
        </div>
        <div id="gameBox">
            <div class="background"></div>
            <span class="score">0</span>
            
        </div>
        <div id="gameOver">
            <p>游戏结束</p>
            <p>你的得分</p>
            <p class="finalScore"></p>
            <span class="restart">重新开始</span>
        </div>
    </div>
    <script>
        (function(){
            let wrap = document.querySelector("#wrap");
            let levelBtn = document.querySelectorAll("#mainBox li");
            let gameBox = document.querySelector("#gameBox");
            let mainBox = document.querySelector("#mainBox");
            let gameOverBox = document.querySelector("#gameOver");
            let bg = document.querySelector("#gameBox .background");
            let score = document.querySelector("#gameBox .score");
            let finalScore= document.querySelector(".finalScore");
            let restartBtn = document.querySelector(".restart");
            let bulletRate,bulletSpeed,enemyRate,enemySpeed,bgSrc,bigEnemy;
            const gameLevel = [
                    {
                        bulletRate : 10,
                        bulletSpeed : 10,
                        enemyRate : 30,
                        enemySpeed : 5,
                        bigEnemy : 5,
                        bgSrc : "./images/bg_2.jpg"
                    },
                    {
                        bulletRate : 15,
                        bulletSpeed : 8,
                        enemyRate : 20,
                        enemySpeed : 6,
                        bigEnemy : 4,
                        bgSrc : "./images/bg_3.jpg"
                    },
                    {
                        bulletRate : 20,
                        bulletSpeed : 6,
                        enemyRate : 15,
                        enemySpeed : 8,
                        bigEnemy : 3,
                        bgSrc : "./images/bg_4.jpg"
                    },

            ];
            class Sprite {
                constructor(imgSrc, width, height, x = 0, y = 0) {
                    this.img = new Image();
                    this.img.src = imgSrc;
                    this.img.width = width;
                    this.img.height = height;
                    this.img.style.position = "absolute";
                    this.img.style.left = x + "px";
                    this.img.style.top = y + "px";
                }
                initialze() {
                    gameBox.appendChild(this.img);
                }
                remove() {
                    gameBox.removeChild(this.img);
                }
            }
            class Player extends Sprite {
                constructor(x, y) {
                    super("./images/plane_0.png", 50, 50, x - wrap.offsetLeft - 25, y - wrap.offsetTop - 25);
                    this.initialze();
                    this.initEvent();
                }
                playerMove(x, y) {
                     x = Math.max(-this.img.width / 2, x);
                     x = Math.min(x, wrap.clientWidth - this.img.width / 2);
                     y = Math.max(0, y);
                     y = Math.min(y, wrap.clientHeight - this.img.height / 2);
                     this.img.style.top = y + "px";
                     this.img.style.left = x + "px";
                }
                initEvent() {
                    document.addEventListener("mousemove", function(ev) {
                        let x = ev.clientX - wrap.offsetLeft - this.img.width / 2;
                        let y = ev.clientY - wrap.offsetTop - this.img.height / 2;
                        this.playerMove(x, y);
                    }.bind(this))
                }
                shoot() {
                    let bullet = new Bullet();
                    let x = this.img.offsetLeft + this.img.width / 2 - bullet.img.width / 2;
                    let y = this.img.offsetTop - 10
                    bullet.init(x, y);
                }
            }

            class Bullet extends Sprite {
                constructor(x, y) {
                    super("./images/fire.png", 10, 20);
                }
                init(x, y) {
                    this.img.style.top = y + "px";
                    this.img.style.left = x + "px";
                    this.initialze();
                    gameData.bullets.push(this);
                }
                update() {
                    this.img.style.top = this.img.offsetTop - bulletSpeed + "px";
                    if(this.img.offsetTop < 0) {
                        gameData.removeBullet(this);
                        this.remove();
                    }
                }
            }
            
            function rdn(start, end) {
                return Math.floor(Math.random()*(end - start)) + start ;
            }
            class Enemy extends Sprite {
                constructor() {
                    let width = gameData.frame % (enemyRate * bigEnemy) === 0 ? 130 : 50;
                    let height = gameData.frame % (enemyRate * bigEnemy) === 0 ? 100 : 40;
                    let imgSrc = gameData.frame % (enemyRate * bigEnemy) === 0 ? "./images/enemy_big.png" : "./images/enemy_small.png";
                    let hp = gameData.frame % (enemyRate * bigEnemy) === 0 ? rdn(5, 8) : rdn(1, 3);
                    let score = gameData.frame % (enemyRate * bigEnemy) === 0 ? 50 : 10;
                    let x = rdn(0, wrap.clientWidth) - width / 2;
                    super(imgSrc, width, height, x, -height);
                    this.speed = rdn(1, 3);
                    this.hp = hp;
                    this.score = score;
                    this.init();
                }
                init() {
                    this.initialze();
                    gameData.enemys.push(this);
                }
                update() {
                    this.img.style.top = this.img.offsetTop + enemySpeed + this.speed + "px";
                    if(this.img.offsetTop > wrap.clientHeight) {
                        gameData.removeEnemy(this);
                        this.remove();
                    }
                }
                boom() {
                    this.img.src = "./images/boom_big.png";
                    this.img.style.transition = ".5s";
                    this.img.style.opacity = "0";
                    this.img.addEventListener("transitionend", function(){
                        this.remove();
                    }.bind(this))
                }
            }

            class GameData {
                reset() {
                    this.frame = 0;
                    this.score = 0;
                    this.bullets = [];
                    this.enemys = [];
                    this.gameOver = false;
                }
                removeBullet(bullet) {
                    let temp = this.bullets.indexOf(bullet);
                    this.bullets.splice(temp, 1);
                }
                removeEnemy(enemy) {
                    let temp = this.enemys.indexOf(enemy);
                    this.enemys.splice(temp, 1);
                }
            }
            function isImpact(ele, otherEle) {
               return ((otherEle.img.offsetLeft + otherEle.img.width >= ele.img.offsetLeft) && (otherEle.img.offsetLeft <= ele.img.offsetLeft + ele.img.width)
                && (otherEle.img.offsetTop + otherEle.img.height >= ele.img.offsetTop) && (otherEle.img.offsetTop <= ele.img.offsetTop + ele.img.height))
            }
            class Play {
                constructor(x, y) {
                    this.gameBoxInit();
                    this.player = new Player(x, y);
                    this.bg = new Bg();
                    this.bindLoop = this.loop.bind(this);
                    gameData.reset();
                    this.loop();
                }
                gameBoxInit() {
                    gameData.reset();
                    mainBox.style.display = "none";
                    gameBox.style.display = "block";
                    gameOverBox.style.display = "none";
                }
                update() {
                    gameData.enemys.concat(gameData.bullets).forEach(ele => {
                        ele.update();
                    });
                    this.bg.update();
                    score.innerHTML = gameData.score;
                }
                impactDetecition() {
                    gameData.enemys.forEach(function(ele) {
                        for(let i = 0; i < gameData.bullets.length;) {
                            if(isImpact(ele, gameData.bullets[i])) {
                                gameData.bullets[i].remove();
                                gameData.removeBullet(gameData.bullets[i]);
                                ele.hp--;
                                if(ele.hp === 0) {
                                    gameData.score += ele.score;
                                    ele.boom();
                                    gameData.removeEnemy(ele);
                                }
                            }else {
                                i++;
                            }
                        }
                        if(isImpact(ele, this.player)) {
                            gameData.gameOver = true;
                        }
                    }.bind(this))
                }
                gameOver() {
                    mainBox.style.display = "none";
                    gameBox.style.display = "none";
                    gameOverBox.style.display = "block";
                    finalScore.innerHTML = gameData.score;
                    this.player.remove();
                    gameData.enemys.concat(gameData.bullets).forEach(ele => {
                        ele.remove();
                    })
                }
                loop() {
                    if(!gameData.gameOver) {
                        gameData.frame++;
                        this.update();
                        this.impactDetecition();
                        if(gameData.frame % enemyRate === 0) {
                            let enemy = new Enemy();
                        }
                        if(gameData.frame % bulletRate === 0) {
                            this.player.shoot();
                        }
                        requestAnimationFrame(this.bindLoop)
                    }else {
                        this.gameOver();
                    }
                }
            }
            class Bg {
                constructor() {
                    bg.style.background = `url(${bgSrc})`;
                    this.bgTop = -wrap.clientHeight;
                }
                update() {
                    this.bgTop++;
                    this.bgTop = this.bgTop === 0 ? -wrap.clientHeight : this.bgTop;
                    bg.style.top = this.bgTop + "px";
                }
            }
            
            class UI {
                constructor() {
                    this.selectLevel();
                    this.restart();
                }
                selectLevel() {
                    [...levelBtn].forEach((ele, index) => {
                        ele.onclick = function(ev) {
                            let x = ev.pageX;
                            let y = ev.pageY;
                            bulletRate = gameLevel[index].bulletRate;
                            bulletSpeed = gameLevel[index].bulletSpeed;
                            enemyRate = gameLevel[index].enemyRate;
                            enemySpeed = gameLevel[index].enemySpeed;
                            bigEnemy = gameLevel[index].bigEnemy;
                            bgSrc = gameLevel[index].bgSrc;
                            this.play = new Play(x, y);
                        }
                    })
                }
                restart() {
                    restartBtn.onclick = function() {
                        mainBox.style.display = "block";
                        gameOverBox.style.display = "none";
                    }
                }
            }
            let gameData = new GameData();
            let ui = new UI();
        })()
    </script>
</body>
</html>